<script setup lang="ts">
import { ref, onMounted, onBeforeUnmount } from 'vue';
import { gantt } from 'dhtmlx-gantt';
import type { Task, Link } from 'dhtmlx-gantt';
import 'dhtmlx-gantt/codebase/dhtmlxgantt.css';

const gantt_here = ref<HTMLDivElement | null>(null);
const timeFormat = gantt.date.date_to_str('%Y-%m-%d %H:%m:%s');
const getWeekOfMonthNumber = (date: Date) => {
  let adjustedDate = date.getDate() + date.getDay();
  let prefixes = ['0', '1', '2', '3', '4', '5'];
  return parseInt(prefixes[0 | (adjustedDate / 7)]) + 1;
};
interface Props {
  tasks: {
    data: Task[];
    links: Link[];
  };
}

const props = defineProps<Props>();
const emits = defineEmits<{
  (e: 'taskDblClick', id: string | number): void;
}>();
const initGantt = () => {
  // 清空之前的配置
  gantt.clearAll();
  // 设置中文
  gantt.i18n.setLocale('cn');
  gantt.plugins({
    tooltip: true,
    marker: true,
  });
  // 设置用于从数据集解析数据并将日期发送回服务器的日期格式
  gantt.config.date_format = '%Y-%m-%d %H:%i:%s';
  // 定义时间刻度的配置
  gantt.config.fit_tasks = true;
  gantt.config.scale_height = 72;
  gantt.config.row_height = 30;
  gantt.config.scales = [
    { unit: 'month', step: 1, format: '%Y年%m月' },
    {
      unit: 'week',
      step: 1,
      format: (date) => 'Week #' + getWeekOfMonthNumber(date),
    },
    {
      unit: 'day',
      step: 1,
      format: '%j  周%D',
      css: setWeekendCss,
    },
  ];
  gantt.config.sort = true; // 启用排序
  gantt.config.autosize = true; //自适应尺寸
  gantt.config.autofit = true; // 表格列宽自适应
  gantt.config.autoscroll = true; // 把任务或者连线拖拽到浏览器屏幕外时，自动触发滚动效果
  gantt.config.drag_resize = false; // 禁止拖动调整任务时间长度
  gantt.config.drag_links = false; // 禁止拖动链接
  gantt.config.drag_move = false; // 禁止拖动任务
  gantt.config.drag_progress = false; // 禁止拖动任务进度条来改变任务进度
  gantt.config.smart_scales = true; // 指定仅在屏幕上呈现时间刻度的可见部分
  gantt.config.work_time = true; // removes non-working time from calculations
  // 高亮显示时间轴上的周六日
  heightLightWeekend();
  // 添加today标记
  addToday();

  // 设置tooltips显示时间
  gantt.config.tooltip_timeout = 50;

  gantt.config.columns = [
    {
      name: 'text',
      label: '任务名称',
      tree: true,
      width: 140,
      template: (task: Task) =>
        `<div title="${task.text}" class="text-ellipsis overflow-hidden">${task.text}</div>`,
    },
    { name: 'start_date', label: '开始时间', align: 'center' },
    {
      name: 'duration',
      label: '任务时长',
      align: 'center',
      template: (task: Task) => `<span>${task.duration}天</span>`,
    },
  ];

  // 设置DOM节点
  gantt.init(gantt_here.value as HTMLDivElement);
  // 解析数据
  gantt.parse(props.tasks);
};

gantt.attachEvent('onGanttReady', () => {
  let tooltips = gantt.ext.tooltips;
  tooltips.tooltip.setViewport(gantt.$task_data); // 限制tooltips的显示区域
  gantt.templates.tooltip_text = (start, end, task) => {
    let descStr = task.desc ? `<b>任务描述：</b>${task.desc}<br/>` : '';
    return `<b>任务名称：</b>${task.text}<br/><b>开始时间：</b>${timeFormat(
      start,
    )}<br/><b>结束时间：</b>${timeFormat(end)}<br/>${descStr}`;
  };

  tooltips.tooltipFor({
    selector: '.gantt_scale_cell',
    html: (event, _node) => {
      const relativePosition = gantt.utils.dom.getRelativeEventPosition(
        event,
        gantt.$task_scale,
      ) as { x: number; y: number };

      return gantt.templates.tooltip_date_format(
        gantt.dateFromPos(relativePosition.x),
      );
    },
  });

  tooltips.tooltipFor({
    selector: '.gantt_task_link',
    html: (_event, node) => {
      var linkId = node.getAttribute(gantt.config.link_attribute);
      if (linkId) {
        var link = gantt.getLink(linkId);
        var from = gantt.getTask(link.source);
        var to = gantt.getTask(link.target);
        return [
          '<b>Link:</b> ' + linkTypeToString(link.type),
          '<b>From: </b> ' + from.text,
          '<b>To: </b> ' + to.text,
        ].join('<br>');
      }
    },
  });
});

// 双击任务查看详情和编辑
gantt.attachEvent('onTaskDblClick', function (id, _e) {
  emits('taskDblClick', id);
  return false;
});

// 向时间轴上添加一个 today 标记
function addToday() {
  let dateToStr = gantt.date.date_to_str('%Y年%m月%d日');
  let today = new Date();
  gantt.addMarker({
    start_date: today,
    css: 'today',
    text: 'Today',
    title: 'Today: ' + dateToStr(today),
  });
}

// 高亮显示时间轴上的周六日
function heightLightWeekend() {
  gantt.templates.scale_cell_class = setWeekendCss;
  gantt.templates.timeline_cell_class = (task, date) => {
    if (!gantt.isWorkTime({ task: task, date: date })) {
      return 'weekend';
    }
    return '';
  };
}

// 设置周六日的css样式类名
function setWeekendCss(date: Date) {
  if (!gantt.isWorkTime(date)) {
    return 'weekend';
  }
  return '';
}

onMounted(() => {
  initGantt();
});

onBeforeUnmount(() => {});

/**
 *
 * @param linkType
 *  "0" -  'finish_to_start'.
    "1" -  'start_to_start'.
    "2" -  'finish_to_finish'.
    "3" -  'start_to_finish'.
 */
function linkTypeToString(linkType: string) {
  switch (linkType) {
    case gantt.config.links.start_to_start:
      return 'Start to start';
    case gantt.config.links.start_to_finish:
      return 'Start to finish';
    case gantt.config.links.finish_to_start:
      return 'Finish to start';
    case gantt.config.links.finish_to_finish:
      return 'Finish to finish';
    default:
      return '';
  }
}

defineExpose({
  gantt,
});
</script>

<script lang="ts">
export default {
  name: 'DhtmlxGantt',
};
</script>

<template>
  <div ref="gantt_here"></div>
</template>

<style>
.gantt_grid_scale .gantt_grid_head_cell,
.gantt_task .gantt_task_scale .gantt_scale_cell {
  font-weight: bold;
  font-size: 14px;
  color: rgba(0, 0, 0, 0.7);
  cursor: default;
}

.gantt_tooltip {
  font-size: 13px;
  line-height: 16px;
}

.weekend {
  background: #f0efeb !important;
}

.today {
  background-color: #ffb703 !important;
}
</style>
